今天開始來建立第一個真正屬於我們的 Kubernetes Workload。
但先回答一個很重要的問題。
既然 Kubernetes 管 Container:
為什麼最小單位不是 Container,而是 Pod?
最簡單情況:
Pod
└── Container
所以初學時很容易覺得:
Pod 就等於 Container。
其實不是。
一個 Pod 可以有多個 Container:
Pod
├── Main Container
└── Sidecar Container
它是一種在分散式架構與 Kubernetes 中常見的設計模式:將輔助功能從主應用程式中拆分出來,打包成獨立的容器,並與主應用容器部署在同一個 Pod(或同一個生命週期單元)中協同運作。
其名稱來自於摩托車旁的「邊車」(Sidecar)
用這張圖來表示應該就很好理解XD
——主容器就像摩托車本體負責驅動前進(核心業務邏輯),而 Sidecar 就像掛在旁邊的邊車,提供額外支援(監控、日誌收集、代理),兩者同進同退。

又因都位在同一個 Pod,裡面的 Container 便可共享一些資源,例如 Network Namespace。
因此它們可以透過:
localhost
互相溝通。
可以把 Pod 理解成:
Kubernetes 對一組「必須一起生活」的 Container 提供的執行邊界。
但大多數情況,若沒特殊需求.
一個 Pod 還是只有一個主要 Application Container。
先用最快的方式:
kubectl run nginx \
--image=nginx:alpine
查看:
kubectl get pods
可能一開始:
ContainerCreating
稍後變:
Running

這個過程代表:
Pod Object 建立
↓
Scheduler 選 Node
↓
kubelet 收到任務
↓
取得 nginx Image
↓
建立 Container
kubectl get pods -o wide
-o wide 代表:
顯示更多欄位。
除了 Pod Name、Status,還可以看到:
IP
NODE

例如:
nginx Running 10.x.x.x cka-lab-worker
也就是 Scheduler 已經幫我們決定了 Node。
kubectl describe pod nginx
內容很多。


現在不用全部看懂。
先找到:
Node
Containers
Conditions
Events
尤其:
Events
未來非常重要。
如果:
Pod Pending
ImagePullBackOff
Probe Failed
Volume 掛不上
很多線索都會出現在這裡。
kubectl logs nginx
這個指令查看 Container 寫到:
stdout
stderr
的內容。
也就是 Application Log。

kubectl exec -it nginx -- sh
拆開。
kubectl exec
在 Container 裡執行 Command。
-i
保持 stdin。
-t
提供 Terminal。
--
代表:
後面開始是 Container 裡要執行的 Command。
最後:
sh
在 Container 裡啟動 Shell。
進去後:
hostname
可能看到 Pod 名稱。
exit
離開。

我們剛剛:
kubectl run
例如:
kubectl run nginx \
--image=nginx:alpine
幫我做一件事情:
要求 Kubernetes 建立一個名為 nginx 的 Pod,並在裡面運行一個基於 nginx:alpine 映像檔的容器。
這種直接透過指令告訴 Kubernetes 的方式稱為:
Imperative (命令式)
但 Kubernetes 更核心的方式是:
Declarative (宣告式)
先個別建立對應的 yaml:
pod.yaml
例如:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
然後再透過 apply 針對該 yaml 去建立起來
kubectl apply -f pod.yaml
這不是在說:
請執行某個步驟。
而是在說:
我希望 Cluster 裡存在一個符合這個描述的 Pod。
這就是 Desired State。
| 比較維度 | 命令式 (Imperative) | 宣告式 (Declarative) |
|---|---|---|
| 核心觀念 | 怎麼做 (How):逐步下達操作指令 | 期望狀態 (What):定義最終期望的目標狀態 |
| 主要操作 | 直接執行指令(如 run, create, expose) |
撰寫 YAML / JSON 檔案並交由系統對齊狀態 |
| 典型指令 | kubectl run nginx --image=nginx |
kubectl scale deployment/web --replicas=3 |
| 版本控管 (GitOps) | 困難(指令操作不易追蹤歷史變更) | 優良(Manifest 檔案可納入 Git 進行版控與 Code Review) |
| 可重複性與維護性 | 低(重複執行容易報錯,難以稽核) | 高(具備冪等性,適合自動化 CI/CD 流水線) |
| 適用情境 | 臨時排查、快速測試、除錯一次性任務 | 正式環境 (Production)、團隊協作、長期維護的基礎架構 |
幾乎所有 Kubernetes YAML 都會看到:
apiVersion:
kind:
metadata:
spec:
以下進行解釋:
Kubernetes 的 YAML 設定檔(Manifest)主要由這四個頂層欄位組成,針對各欄位的詳細說明與底層邏輯整理如下:
apiVersion 用來告訴 Kubernetes API Server 該用哪一個版本的 API schema 來解析並處理這份定義。
它的格式主要有兩種:
v1
<api-group>/<version>,例如 apps/v1、batch/v1
/api 與 /apis 的底層差別/api 與 /apis 是 Kubernetes API Server 的 RESTful 端點路徑
/api:這是 Core API 的入口(歷史遺留原因,最早的 K8s 資源都在這)。/apis:這是 Named API Groups 的入口(後來新增的、擴展的資源都歸類於此)。Core API Group(又稱 Legacy Group):
API URL 端點:/api/v1
特點:Kubernetes 最早期的核心資源,沒有命名群組(Group name 為空字串 "")。
YAML 寫法:直接寫 apiVersion: v1。
常見資源:Pod、Service、Namespace、ConfigMap、Secret、Node。
Named Groups:
API URL 端點:/apis/<group>/<version>
特點:後來為了模組化與擴充性引入的資源群組。
YAML 寫法:<group>/<version>。
常見範例:
apps/v1(Deployment、StatefulSet、DaemonSet)batch/v1(Job、CronJob)networking.k8s.io/v1(Ingress、NetworkPolicy)rbac.authorization.k8s.io/v1(Role、ClusterRole)查詢小技巧:如果不確定某個資源要填什麼
apiVersion,可以直接在終端機執行:kubectl explain <資源名稱> # 例如:kubectl explain pod 或 kubectl explain deployment第一行就會清楚標示出該資源對應的
VERSION與KIND。
告訴 Kubernetes 你想要建立哪一種物件。
Deployment、Service、Pod,不能寫成全小寫或混用。kind 名稱可能在不同 API 版本有不同定義(例如過去曾有 extensions/v1beta1 的 Deployment,後來演進為 apps/v1)。定義該物件的「身份識別資料」以及附加的管理標籤,主要給人類、Controller 或外部工具進行識別與篩選。
主要包含的欄位:
name(必要):同一個 Namespace 底下,相同 kind 的資源名稱必須唯一。namespace(選填):指定該資源屬於哪個邏輯隔離區塊。若未填寫,預設會部署在當前 Context 所屬的 Namespace(通常是 default)。labels(鍵值對):用於識別與選取。例如給 Service、Deployment 的 selector 來過濾關聯哪些 Pod。annotations(鍵值對):用於附帶額外非識別資訊。通常給監控工具、CI/CD 系統或 Ingress Controller 讀取設定(例如流量路由權重、Prometheus 採集開關)。這是整個 YAML 的核心,代表你期望該物件達到的最終狀態(Desired State)。
宣告式核心:你描述 spec,Kubernetes 的 Controller 就會不斷執行 Reconciliation Loop(是一種不斷將「實際狀態」調整為「期望狀態」的自動化控制機制),努力讓叢集的實際狀態(Current State / status)與 spec 保持一致。
結構依 Resource 而異:不同 kind 的 spec 內容完全不同:
Pod 的 spec:定義有哪些容器(containers)、掛載哪些硬碟(volumes)、重啟策略(restartPolicy)等。
Deployment 的 spec:定義要複製幾份(replicas)、更新策略(strategy)、以及用來產生 Pod 的範本(template)。
Service 的 spec:定義轉發規則(ports)、型態(type: ClusterIP / NodePort / LoadBalancer)、選取哪些 Pod(selector)。
少數例外:並非所有物件都有 spec。例如純粹存放資料的 ConfigMap 與 Secret,主要的設定內容欄位是 data / stringData,就沒有 spec 區塊。
apiVersion: apps/v1 # 具名群組 apps 下的 v1 版本 (端點在 /apis/apps/v1)
kind: Deployment # 大駝峰命名,宣告要建立 Deployment 物件
metadata:
name: nginx-deployment # 物件唯一名稱
labels:
app: my-nginx # 供日後過濾或查詢的標籤
spec: # 期望狀態定義
replicas: 2 # 期望維持 2 個 Pod
selector:
matchLabels:
app: my-nginx
template: # 底下定義 Pod 範本 (屬於 Pod 的 metadata 與 spec)
metadata:
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
kubectl delete pod nginx
再:
kubectl get pods
你會發現:
沒了。
它不會自己回來。
這是一個超級重要的觀察。
因為現在只是:
孤獨的 Pod。
沒有 Controller 管它。
所以:
Pod 死掉
=
真的死掉。
那要如何避免呢?
明天開始,我們就會解決這個問題。